using System;
using System.Collections;
using System.Collections.Generic;
using System.Html;
using System.Runtime.CompilerServices;
using Nsb.Classes;
using ThemeChain;
using jQueryApi;

[Mixin("jQuery.fn")]
public static class jQueryJewlExtensions
{
    //public static jQueryObject Test()
    //{
    //    return jQuery.Current.Each(
    //        delegate
    //        {
    //            Script.Alert("In Yes Plugin");
    //            // TODO: Consume the matched elements
    //        });
    //}

    public static jQueryObject EndEvent(jQueryEvent e)
    {
        DomElement.CancelEvent(e);
        return jQuery.Current;
    }

    public static string GetContent()
    {
        if (jQuery.Current.Is("input[type=text],input[type=password],textarea,select"))
            return jQuery.Current.GetValue() ?? "";
        if (jQuery.Current.Is("input[type=checkbox]"))
            return jQuery.Current.Is(":checked").ToString();
        return jQuery.Current.GetHtml() ?? "";
    }

    public static jQueryObject SetContent(string txt)
    {
        if (jQuery.Current.Is("input[type=text],input[type=password],textarea,select"))
        {
            // Not sure all this is really doing anything except giving more time for the property to change before it is read as html in the dom
            // But it does make it work, for now
            if (jQuery.Current.Is("textarea,"))
                jQuery.Current.Html(txt);
            else
                jQuery.Current.Attribute("value", txt);
            return jQuery.Current.Value(txt);
        }
        if (jQuery.Current.Is("input[type=checkbox]"))
        {
            if (Boolean.Parse(txt))
                return jQuery.Current.Attribute("checked", "checked");
            return jQuery.Current.RemoveAttr("checked");
        }
        return jQuery.Current.Html(txt);
    }

    public static jQueryObject FValueString(string txt)
    {
        if (jQuery.Current.Is("input[type=password]"))
            return jQuery.Current.Value(txt);
        txt = ThemeBase.ParseAnchorsToEdit(txt);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/&nbsp;/g"), " ");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<br\/?>/g"), "\n");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/&lt;/g"), "<");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/&gt;/g"), ">");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/&amp;/g"), "&");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<span style=[""']float:right[""']>(.*?)<\/span>/g"), "<right>$1");

        if (jQuery.Current.Is("input[type=text],textarea,select"))
            return jQuery.Current.Value(txt);
        if (jQuery.Current.Is("input[type=checkbox]"))
        {
            if (Boolean.Parse(txt))
                return jQuery.Current.Attribute("checked", "checked");
            return jQuery.Current.RemoveAttr("checked");
        }
        return jQuery.Current.Html(txt);
    }

    public static jQueryObject FValue(object dta)
    {
        if (jQuery.Current.Is("input[type=checkbox]"))
        {
            if ((bool)Script.Literal("{0}", dta))
                return jQuery.Current.Attribute("checked", "checked");
            return jQuery.Current.RemoveAttr("checked");
        }
        dta = dta == null ? "" : dta.ToString();
        return jQuery.Current.Plugin<Jewl>().FValueString((string)dta);
    }

    public static object GetFValue()
    {
        if (jQuery.Current.Length == 0)
            return "";
        string txt;
        if (jQuery.Current.Is("input[type=text],textarea,select"))
            txt = jQuery.Current.GetValue();
        else if (jQuery.Current.Is("input[type=password]"))
            return jQuery.Current.GetValue();
        else if (jQuery.Current.Is("input[type=checkbox]"))
            return jQuery.Current.Is(":checked");
        else
            return jQuery.Current.GetHtml();
        txt = txt.Trim();

        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<right>(.*?)(\r?\n|$)/g"), "<span style=\"float:right\">$1</span>\n");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<smile>/g"), ThemeBase.Smile);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<ball>/g"), ThemeBase.Bullit);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<heart>/g"), ThemeBase.Hearts);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<spade>/g"), ThemeBase.Spades);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<club>/g"), ThemeBase.Clubs);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<diamond>/g"), ThemeBase.Diamonds);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<sun>/g"), ThemeBase.Sun);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<line>/g"), "&#8212;");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<leftarrow>/g"), "&#8592;");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<rightarrow>/g"), "&#8594;");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<dbllfarrow>/g"), "&#8656;");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/<dblrtarrow>/g"), "&#8658;");
        txt = Strings.SanitizeTags(txt);
        txt = ThemeBase.ParseEditToAnchors(txt);
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/\r/g"), "");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/\n/g"), "<br>");
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/  /g"), "&nbsp; ");    // Spaces to alternating sp &nbsp
        txt = txt.ReplaceRegex((RegularExpression)Script.Literal(@"/  /g"), " &nbsp;");
        //Inform.Debug("Filtered input: {0}", txt);
        return txt.Trim();
    }

    public static string GetFValueString()
    {
        return jQuery.Current.Plugin<Jewl>().GetFValue().ToString();
    }

    public static string GetFEditString()
    {
        string txt1 = jQuery.Current.GetValue();
        string txt2 = jQuery.Current.Plugin<Jewl>().GetFValue().ToString();
        if (Rx.HasEndSpace.Test(txt1))
            txt2 += " ";
        return txt2;
    }

    public static jQueryObject ReFocus()
    {
        jQueryObject ths = jQuery.Current; 
        Window.SetTimeout(delegate { ths.Focus(); }, 1);
        return jQuery.Current;
    }

    public static jQueryObject SwallowAllMouseEvents()
    {
        return jQuery.Current
            .Bind("click.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            .Bind("doubleclick.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            .Bind("mousedown.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            .Bind("mouseup.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            .Bind("mouseover.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            .Bind("mouseenter.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            .Bind("mouseleave.swallowed", delegate(jQueryEvent e) { DomElement.CancelEvent(e); })
            ;
    }

    public static jQueryObject SwallowClick(jQueryEventHandler fn)
    {
        return jQuery.Current
            .Click(fn)
            .MouseDown(DomElement.CancelEvent)
            .DoubleClick(DomElement.CancelEvent)
            .MouseUp(DomElement.CancelEvent)
            ;
    }

    public static jQueryObject VertHtml()
    {
        string value = jQuery.Current.Plugin<Jewl>().GetFValueString();
        jQueryObject f = jQuery.FromHtml("<p/>").Html(value);
        return jQuery.Current.Plugin<Jewl>().FValueString(f.GetHtml());
    }

    public static jQueryObject FitIntoWindow()
    {
        int sctop = jQuery.FromObject(Window.Document).GetScrollTop();
        int scleft = jQuery.FromObject(Window.Document).GetScrollLeft();
        int top = jQuery.Current.GetOffset().Top;
        int left = jQuery.Current.GetOffset().Left;
        top = Math.Max(top, sctop);
        left = Math.Max(left, scleft);
        top = Math.Min(top, sctop + jQuery.FromObject(Window.Self).GetInnerHeight() - jQuery.Current.GetOuterHeight());
        left = Math.Min(left, scleft + jQuery.FromObject(Window.Self).GetInnerWidth() - jQuery.Current.GetOuterWidth());
        jQuery.Current.CSS("top", top + "px");
        return jQuery.Current.CSS("left", left + "px");
    }

    public static jQueryObject CloneToBody(jQueryObject element)
    {
        jQueryObject ths = jQuery.Current;
        jQueryObject el = jQuery.FromHtml("<div class='GigMu MenuZee'/>")
            .AppendTo(Document.Body)
            .Offset(element.GetOffset())
            //.CSS(new Dictionary("top", p.Top, "left", p.Left + element.GetOuterWidth() - ths.GetOuterWidth()))
            ;
        ths
            .AppendTo(el)
            .Show()
            ;
        if (ths.GetDataValue("GigMu1") == null)
            ths
                .Bind("mouseleave.Gears",
                      delegate
                      {
                          ths.Detach();
                          Action fnOff = (Action)ths.GetDataValue("GearsOff");
                          if (fnOff != null)
                              fnOff();
                          el.Remove();
                      }
                )
                ;

        ths.Data("GigMu1", ths);
        ths.Data("GigMu2", el);
        return ths;
    }

    public static Dictionary GetSelection()
    {
        Element el = jQuery.Current[0];
        int start = 0;
        int end = 0;

        if ((bool)Script.Literal("typeof {0}.selectionStart == 'number'", el) && (bool)Script.Literal("typeof {0}.selectionEnd == 'number'", el))
        {
            start = (int)Script.Literal("{0}.selectionStart", el);
            end = (int)Script.Literal("{0}.selectionEnd", el);
        }
        else
        {
            int range = (int)Script.Literal("document.selection.createRange()");

            if (range != 0 && (bool)Script.Literal("{0}.parentElement() == {1}", range, el))
            {
                int len = (int)Script.Literal("{0}.value.length", el);
                string normalizedValue = (string)Script.Literal("{0}.value.replace(/\\r\\n/g, '\\n')", el);

                // Create a working TextRange that lives only in the input
                int textInputRange = (int)Script.Literal("{0}.createTextRange()", el);
                Script.Literal("{0}.moveToBookmark({1}.getBookmark())", textInputRange, range);

                // Check if the start and end of the selection are at the very end
                // of the input, since moveStart/moveEnd doesn't return what we want
                // in those cases
                int endRange = (int)Script.Literal("{0}.createTextRange()", el);
                Script.Literal("{0}.collapse(false)", endRange);

                if ((int)Script.Literal("{0}.compareEndPoints('StartToEnd', {1})", textInputRange, endRange) > -1)
                {
                    start = end = len;
                }
                else
                {
                    start = (int)Script.Literal("-{0}.moveStart('character', -{1})", textInputRange, len);
                    start += (int)Script.Literal("{0}.slice(0, {1}).split('\\n').length - 1", normalizedValue, start);

                    if ((bool)Script.Literal("{0}.compareEndPoints('EndToEnd', {1}) > -1", textInputRange, endRange))
                    {
                        end = len;
                    }
                    else
                    {
                        end = (int)Script.Literal("-{0}.moveEnd('character', -{1})", textInputRange, len);
                        end += (int)Script.Literal("{0}.slice(0, {1}).split('\\n').length - 1", normalizedValue, end);
                    }
                }
            }
        }

        return new Dictionary(
            "start", start,
            "end", end
            );
    }

    public static Dictionary CloneEditCSS()
    {
        jQueryObject el = jQuery.Current;
        jQueryPosition p = el.GetOffset();

        return
            new Dictionary(
                "position", "absolute",
                "margin", 0,
                "top", p.Top,
                "left", p.Left,
                "width", el.GetInnerWidth(),
                "height", el.GetInnerHeight(),
                "border", "none",
                "background", "#FFC",
                "font-family", el.GetCSS("font-family"),
                "font-size", el.GetCSS("font-size"),
                "font-weight", el.GetCSS("font-weight"),
                "text-decoration", el.GetCSS("text-decoration"),
                "padding-top", el.GetCSS("padding-top"),
                "padding-left", el.GetCSS("padding-left"),
                "padding-right", el.GetCSS("padding-right"),
                "padding-bottom", el.GetCSS("padding-bottom"),
                "overflow", "hidden"
                );
    }

    public static jQueryObject DisableLink(bool disable, string title)
    {
        if (disable)
            jQuery.Current.Data("href", jQuery.Current.GetAttribute("href")).RemoveAttr("href");
        else
            jQuery.Current.Attribute("href", (string)jQuery.Current.GetDataValue("href"));
        return jQuery.Current.Attribute("title", title);
    }

    public static jQueryObject FadeInW(int ms, Action fn)
    {
        FileBlobBase.SvCnt++;
        return jQuery
            .Current
            .FadeIn(ms,
                    delegate
                    {
                        if (fn != null)
                            fn();
                        FileBlobBase.SvCnt--;
                    });
    }

    public static jQueryObject FadeOutW(int ms, Action fn)
    {
        FileBlobBase.SvCnt++;
        return jQuery
            .Current
            .FadeOut(ms,
                     delegate
                     {
                         if (fn != null)
                             fn();
                         FileBlobBase.SvCnt--;
                     });
    }

    public static jQueryObject Info(string msg)
    {
        Inform.Log("Jewl.Inform: " + msg);
        return jQuery.Current;
    }

    public static jQueryObject CopyRadius(Dictionary<string, string> css, int xtra, string units)
    {
        units = units ?? ThemeBase.StdUnits;
        Dictionary css2 = new Dictionary();
        int rad = Number.ParseInt((string)Type.GetField(css, "border-radius") ?? "0") + xtra;
        css2["border-radius"] = rad + units;
        css2["-moz-border-radius"] = rad + units;
        css2["-webkit-border-radius"] = rad + units;
        return jQuery.Current.CSS(css2);
    }
}

[Imported]
public sealed class Jewl : jQueryObject
{
    public jQueryObject EndEvent(jQueryEvent e)
    {
        return null;
    }

    public string GetContent()
    {
        return null;
    }

    public jQueryObject SetContent(string text)
    {
        return null;
    }

    public jQueryObject FValueString(string value)
    {
        return null;
    }

    public jQueryObject FValue(object value)
    {
        return null;
    }

    public string GetFValueString()
    {
        return null;
    }

    public string GetFEditString()
    {
        return null;
    }

    public object GetFValue()
    {
        return null;
    }

    public jQueryObject ReFocus()
    {
        return null;
    }

    public jQueryObject SwallowAllMouseEvents()
    {
        return null;
    }

    public jQueryObject VertHtml()
    {
        return null;
    }

    public jQueryObject FitIntoWindow()
    {
        return null;
    }

    public Dictionary GetSelection()
    {
        return null;
    }

    public Dictionary CloneEditCSS()
    {
        return null;
    }

    public jQueryObject DisableLink(bool disable, string title)
    {
        return null;
    }

    [AlternateSignature]
    public extern jQueryObject FadeInW(int ms);

    public jQueryObject FadeInW(int ms, Action fn)
    {
        return null;
    }

    [AlternateSignature]
    public extern jQueryObject FadeOutW(int ms);

    public jQueryObject FadeOutW(int ms, Action fn)
    {
        return null;
    }

    public void Position(Dictionary options)
    {
    }

    public jQueryObject Draggable(Dictionary options)
    {
        return null;
    }

    public jQueryObject Droppable(Dictionary options)
    {
        return null;
    }

    public jQueryObject CloneToBody(jQueryObject Element)
    {
        return null;
    }

    public jQueryObject RemoveFromBody()
    {
        return null;
    }

    public jQueryObject SwallowClick(jQueryEventHandler fn)
    {
        return null;
    }

    [AlternateSignature]
    public extern string SelecTo();

    /// <summary>
    /// Generate a jQuery selector for this element
    /// </summary>
    /// <param name="options">
    /// bind = (jQueryObject || Element) container to look inside
    /// href = (bool) if to include href in selector
    /// hastext = (bool) if to add a :hastext() filter
    /// parent = (bool) build to parent only
    /// classFilter = (RegularExpression) match of classes to remove from selector. Example: /^(invisi|group)$/
    /// </param>
    /// <returns>Selector as a string</returns>
    public string SelecTo(Dictionary options)
    {
        return null;
    }

    public jQueryObject SetSelection(int start, int end)
    {
        return null;
    }

    public jQueryObject CarotToEnd()
    {
        return null;
    }

    public Dictionary GetStyles()
    {
        return null;
    }

    public jQueryObject Info(string msg)
    {
        return null;
    }

    [AlternateSignature]
    public extern jQueryObject CopyRadius(Dictionary css, int xtra);

    public jQueryObject CopyRadius(Dictionary css, int xtra, string units)
    {
        return null;
    }
}

public sealed class jQueryUiObject
{
    public jQueryObject draggable;
    public jQueryObject helper;
    public jQueryPosition position;
    public jQueryPosition offset;
    public jQueryEvent e; // We fill so could be null
}